package com.hoe.tsb.fraft.serialize;

import com.hoe.tsb.fraft.enums.MessageType;
import com.hoe.tsb.fraft.enums.RoleType;
import com.hoe.tsb.fraft.event.msg.FRaftMessage;
import com.hoe.tsb.fraft.exception.FRaftException;

/**
 * <p>
 * 字节数组序列化
 * </p>
 *
 * @author hoe
 * @version 1.0
 * @date 2022/11/23
 */
public class BytesSerializer implements Serializer {

    /**
     * FRaftMessage的头部占用字节数
     */
    private final int HEADER_BYTE_LEN = 5;

    @Override
    public byte[] serialize(FRaftMessage t) {
        MessageType type = t.getType();
        if (type == null) {
            throw new FRaftException("需要序列化的FRaft消息中MessageType字段不能为空");
        }


        //消息体可能为空
        byte[] body = t.getBody();
        if (body == null) {
            body = new byte[HEADER_BYTE_LEN];
        } else {
            byte[] newBody = new byte[body.length + HEADER_BYTE_LEN];
            System.arraycopy(body, 0, newBody, HEADER_BYTE_LEN, body.length);
            body = newBody;
        }

        //消息类型放在字节数组的首位
        Integer typeOrdinal = type.serialize(type);
        body[0] = typeOrdinal.byteValue();
        //其次3.5个字节存放term
        body[1] = (byte) ((t.getTerm() >> 20) & 0xFF);
        body[2] = (byte) ((t.getTerm() >> 12) & 0xFF);
        body[3] = (byte) ((t.getTerm() >> 4) & 0xFF);
        body[4] = (byte) ((t.getTerm() << 4) & 0xF0);
        //其次半字节存放角色类型
        //因为客户端消息角色类型可能为空，此时用1111代替
        body[4] = (byte) (body[4] | RoleType.ser(t.getRoleType()));

        return body;
    }

    @Override
    public FRaftMessage deserialize(byte[] buf) {
        int minLen = HEADER_BYTE_LEN;
        MessageType messageType = MessageType.des((int) buf[0]);
        int term = 0;
        term = term | ((buf[1] & 0xFF) << 20);
        term = term | ((buf[2] & 0xFF) << 12);
        term = term | ((buf[3] & 0xFF) << 4);
        term = term | (buf[4] >>> 4);
        RoleType roleType = RoleType.des(buf[4] & 0xF);

        byte[] data = null;
        if (buf.length > minLen) {
            data = new byte[buf.length - minLen];
            System.arraycopy(buf, minLen, data, 0, data.length);
        }

        //此时并没有设置node，需要在exchanger当中进行赋值
        return new FRaftMessage(messageType, term, roleType, data);
    }

}
